/*
 * @lc app=leetcode id=79 lang=cpp
 *
 * [79] Word Search
 */
class Solution {
private:
    vector<vector<int>> ops = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}};
public:

    bool exist(vector<vector<char>>& board, string word) {
        int wlen = word.size();
        if (wlen < 1) {
            return true;
        }

        int row = board.size();
        if (row < 1) {
            return false;
        }

        int col = board[0].size();

        deque<vector<int>> path; 
        for (int i=0; i<row; i++) {
            for (int j=0; j<col; j++) {
                if (board[i][j] == word[0]) {
                    path.push_back({i,j});
                    if (findWord(board, i, j, word, 0, path)) {
                        return true;
                    }
                    path.pop_back();
                }
            }
        }

        return false;
    }

    bool findWord(vector<vector<char>>& board, int x, int y, string& word, int wi, deque<vector<int>>& path) {
        int row = board.size();
        int col = board[0].size();

        if (wi == word.size()) {
            return true;
        }
        
        if (board[x][y] != word[wi]) {
            return false;
        }

        if (wi + 1 == word.size()) {
            return true;
        }

        for (auto op : ops) {
            int nextX = op[0]+x;
            int nextY = op[1]+y;
            if (validPath(nextX, nextY, row, col) && validPath(path, nextX, nextY)) {
                path.push_back({nextX, nextY});
                if (findWord(board, nextX, nextY, word, wi+1, path)) {
                    return true;
                }
                path.pop_back();
            }
        }
        return false;
    }

    bool validPath(int x, int y, int row, int col) {
        return x >= 0 && x < row && y >= 0 && y < col;
    }

    bool validPath(deque<vector<int>>& path, int x, int y) {

        for (int i=path.size()-1; i>=0; i--) {
            auto& p = path[i];
            if (x == p[0] && y == p[1]) 
                return false;
        }

        return true;
    }
};

